package com.domainlanguage.money;

import com.domainlanguage.time.ImmutableCompositeUserType;
import static org.hibernate.Hibernate.BIG_DECIMAL;
import static org.hibernate.Hibernate.CURRENCY;
import org.hibernate.HibernateException;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.type.Type;

import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Currency;
import java.util.Properties;

/**
 * Hibernate user type for {@link com.domainlanguage.money.Money}.
 *
 * Amount is backed by Hibernate type {@link org.hibernate.Hibernate.BIG_DECIMAL},
 * currency by Hibernate type {@link org.hibernate.Hibernate.CURRENCY}.
 *
 * @see java.util.Currency
 *  
 */
public class MoneyType extends ImmutableCompositeUserType {

    private static final String[] PROPERTY_NAMES = {"amount", "currency"};
    private static final Type[] PROPERTY_TYPES = {BIG_DECIMAL, CURRENCY};

    @Override
    public String[] getPropertyNames() {
        return PROPERTY_NAMES;
    }

    @Override
    public Type[] getPropertyTypes() {
        return PROPERTY_TYPES;
    }

    @Override
    public Object getPropertyValue(final Object component, final int property) throws HibernateException {
        if (component == null) {
            return null;
        }

        final Money money = (Money) component;
        switch (property) {
            case 0:
                return money.breachEncapsulationOfAmount();
            case 1:
                return money.breachEncapsulationOfCurrency();
            default:
                throw new HibernateException("Property index " + property + " is out of bounds. " +
                        "Properties are: " + Arrays.toString(getPropertyNames()));
        }
    }

    @Override
    public Class returnedClass() {
        return Money.class;
    }

    @Override
    public Object nullSafeGet(final ResultSet rs, final String[] names, final SessionImplementor session, final Object owner) throws HibernateException, SQLException {
        final BigDecimal amount = (BigDecimal) BIG_DECIMAL.nullSafeGet(rs, names[0]);
        final Currency currency = (Currency) CURRENCY.nullSafeGet(rs, names[1]);
        if (amount == null || currency == null) {
            return null;
        }

        return Money.valueOf(amount, currency);
    }

    @Override
    public void nullSafeSet(final PreparedStatement st, final Object value, final int index, final SessionImplementor session) throws HibernateException, SQLException {
        final Money money = (Money) value;
        final BigDecimal amount = (money == null) ? null : money.breachEncapsulationOfAmount();
        final Currency currency = (money == null) ? null : money.breachEncapsulationOfCurrency();

        BIG_DECIMAL.nullSafeSet(st, amount, index);
        CURRENCY.nullSafeSet(st, currency, index + 1);
    }
}
